﻿#region [===== Using =====]
using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
#endregion

namespace TPG.SharePoint.Workflow
{
    /// <summary>
    /// A base page for workflow instantiation forms.
    /// </summary>
    /// <remarks>
    /// <para>This class implements the generic framework code for handling 
    /// workflow instantiations. The following tasks are performed by this 
    /// class.</para>
    /// <list type="bullet">
    ///     <item>Starting a workflow on a list item using an association and 
    ///     specific instantiation data.</item>
    /// </list>
    /// <para>An <see cref="InstantiationPage" /> is an ASPX page displayed 
    /// during workflow instantiation (when a workflow is started by an 
    /// end-user inside the SharePoint UI). As the implementer of workflow 
    /// instantiation forms you can derive from this class to implement the 
    /// generic aspects of the workflow form. The non-generic data contained 
    /// in the form is available as a <see cref="ConfigurationData" /> object.
    /// Using the <see cref="InstantiationPage.CommitInstantiation" /> method 
    /// you start the workflow and provide custom data. The 
    /// <see cref="InstantiationPage.CancelInstantiation" /> method redirects the 
    /// user back to the workflow settings page without storing any data and 
    /// without starting the workflow. Data stored during workflow association
    /// can be retrieved using the 
    /// <see cref="InstantiationPage.GetAssociationData" /> method.</para>
    /// </remarks>
    public abstract class InstantiationPage 
        : ListWorkflowPage
    {
        #region [===== Properties =====]
        /// <summary>
        /// Gets the name of the workflow.
        /// </summary>
        protected string WorkflowName
        {
            get { return WorkflowAssociationInternal.Name; }
        }

        /// <summary>
        /// Gets the <see cref="AssociationType" /> indicating with what
        /// kind of target the workflow is associated.
        /// </summary>
        protected AssociationType AssociationType
        {
            get
            {
                return WorkflowAssociationInternal.ParentContentType != null ?
                    AssociationType.ContentType : AssociationType.List;
            }
        }

        /// <summary>
        /// Gets the name of the target with which the association is 
        /// performed.
        /// </summary>
        /// <remarks>
        /// For list associations this method returns the list title, otherwise
        /// the content-type name is returned.
        /// </remarks>
        protected string AssociationTargetName
        {
            get
            {
                SPWorkflowAssociation association = WorkflowAssociationInternal;
                return association.ParentContentType != null ?
                    association.ParentContentType.Name :
                    association.ParentList.Title;
            }
        }

        /// <summary>
        /// Gets the title of the <see cref="SPListItem" /> for which
        /// the instantiation page is displayed.
        /// </summary>
        protected string ListItemTitle
        {
            get 
            {
                string title = null;
                if (List.BaseType == SPBaseType.DocumentLibrary)
                {
                    title = ListItem.Name;
                }
                else
                {
                    title = ListItem.Title;
                }
                return title;
            }
        }

        /// <summary>
        /// Gets the <see cref="SPListItem" /> that the workflow instance is 
        /// operating on.
        /// </summary>
        protected SPListItem ListItem
        {
            get { return SPContext.Current.ListItem; }
        }

        Guid TemplateID
        {
            get { return QueryStringParser.GetParameter<Guid>("TemplateID"); }
        }

        Guid ListID
        {
            get { return QueryStringParser.GetParameter<Guid>("List"); }
        }

        int ListItemID
        {
            get { return QueryStringParser.GetParameter<int>("ID"); }
        }

        string Source
        {
            get { return QueryStringParser.GetParameter<String>("Source"); }
        }
        #endregion

        #region [===== Internal methods =====]
        internal override SPWorkflowAssociation GetWorkflowAssociation()
        {
            return SPWorkflowAssociationCollection.GetAssociationForListItemById(
                ListItem, TemplateID);
        }

        internal override SPWorkflowTemplate GetWorkflowTemplate()
        {
            return WorkflowAssociationInternal.BaseTemplate;
        }

        internal override WorkflowMetaData GetMetaData()
        {
            SPWorkflowAssociation association = WorkflowAssociationInternal;
            bool isAssociationWithContentType = association.ParentContentType != null;
            return new InstantiationMetaData()
            {
                AssociationTargetName = AssociationTargetName,
                AssociationType = AssociationType,
                InstantiationTargetName = ListItemTitle,
                PageType = PageType.Instantiation,
                WorkflowName = WorkflowName,
                ListItem = ListItem
            };
        }
        #endregion

        #region [===== Protected methods =====]
        /// <summary>
        /// Starts a new workflow instance and then redirects to the page 
        /// defined in the Source url query string parameter.
        /// </summary>
        /// <param name="data">The data to pass to the workflow instance as 
        /// instantiation data.</param>
        /// <exception cref="ArgumentNullException">Thrown when the 
        /// <paramref name="data"/> is not specified.</exception>
        protected void CommitInstantiation(ConfigurationData data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }
            using (SPLongOperation operation = new SPLongOperation(this))
            {
                operation.LeadingHTML = GetProgressHtml();
                operation.Begin();
                this.Web.Site.WorkflowManager.StartWorkflow(
                    ListItem, 
                    WorkflowAssociationInternal, 
                    data.NativeData);
                
                operation.End(this.List.DefaultViewUrl, 
                    SPRedirectFlags.UseSource, this.Context, null);
            }
        }

        /// <summary>
        /// Cancels the instantiation by redirecting to the page
        /// in the source url parameter or the list's default view.
        /// </summary>
        protected void CancelInstantiation()
        {
            SPUtility.Redirect(this.List.DefaultViewUrl,
                SPRedirectFlags.UseSource, Context);
        }

        /// <summary>
        /// Gets the data from the workflow association.
        /// </summary>
        /// <returns>The workflow association data.</returns>
        protected ConfigurationData GetAssociationData()
        {
            return ConfigurationData.CreateUsingStringData(
                this.WorkflowAssociationInternal.AssociationData);
        }

        /// <summary>
        /// Returns a metadata property from the workflow template.
        /// </summary>
        /// <param name="name">The name of the property to retrieve.</param>
        /// <returns>The value of the property.</returns>
        protected object GetWorkflowTemplateData(string name)
        {
            return WorkflowTemplateInternal[name];
        }

        /// <summary>
        /// Gets the HTML to display in the progress screen.
        /// </summary>
        /// <returns>A string of HTML, without the HTML, 
        /// BODY or FORM tags</returns>
        protected virtual string GetProgressHtml()
        {
            return null;
        }
        #endregion
    }
}
